/******************************************/
/*********** Connection Wrapper ***********/
/******************************************/
/*
* FILE: connWrapper.c
* AUTHOR: Ram Subramanian and Nicolas Papin
* PROJECT: CS 3251 Project 2 - Professor Traynor
* DESCRIPTION: Shared connection functions.
* PURPOSE: Provides some abstractions for connection handling for use in GPStrac.
*/

#include <stdlib.h>
#include <string.h>
#include "gpsTrac.h"
#include "gpsConn.h"

//
// INITIALIZATION/CONNECTION
//
/*
* Creates a socket and connects to a given server on a given port.
* This is implemented using TCP.
* Returns the socket handle.
*/
//int clientConnect(char *ipAddr, short port)
//{
//    int sock; /* The socket. */
//    struct sockaddr_in serv_addr;   /* The server address */
//
//    /* Create the socket */
//    if((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
//        dieWithMessage("socket() failed");
//
//    /* Initialize the server addr structure. */
//    memset(&serv_addr, 0, sizeof(serv_addr));
//    serv_addr.sin_family = PF_INET;
//    serv_addr.sin_addr.s_addr = inet_addr(ipAddr);
//    serv_addr.sin_port = htons(port);
//
//    if(connect(sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
//        dieWithMessage("connect() failed");
//
//    printf("Connected to server at %s on port %d\n", inet_ntoa(*(struct in_addr *)&serv_addr.sin_addr.s_addr), ntohs(serv_addr.sin_port));
//
//    return sock;
//}

/*
* Creates a server listening on the specified port that accepts connections from any interface.
* Implemented using TCP.
* Returns the socket handle.
*/
int serverInit(int port, int maxPending)
{
    int sock; /* The socket */
    struct sockaddr_in serv_addr; /* The server address */

    /* Create the socket. */
    if((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
        dieWithMessage("[LOG] socket() failed");

    /* Initialize the server addr listening on all ports. */
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = PF_INET;
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    serv_addr.sin_port = htons(port);

    /* Bind socket to address structure. */
    if(bind(sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
        dieWithMessage("[LOG] bind() failed");

    /* Listen for incoming connections */
    if(listen(sock, maxPending) < 0)
        dieWithMessage("[LOG] listen() failed");

    printf("[LOG] Server successfully started.\n[LOG] Now listening for connections on port %d.\n", ntohs(serv_addr.sin_port));

    return sock;
}

/*
* Accepts a connection for the server and creates a new thread with the specified function to handle the client connection.
* Returns 0 on success and any other value otherwise.
*/
int handleConnection(int servSocket, void *(*handler)(void *))
{
    int *clientSocket = malloc(sizeof(int));
    pthread_t newThread;
    struct sockaddr_in clientAddr;
    socklen_t clientAddrLen;

    /* Accept the connection. */
    if((*clientSocket = accept(servSocket, (struct sockaddr *) &clientAddr, &clientAddrLen)) < 0)
		return -1;

    printf("[LOG] Accepted connection from %s on socket %d\n", inet_ntoa(*(struct in_addr*)&clientAddr.sin_addr.s_addr), *(int *)clientSocket);

    /* Create a new thread to handle the connection. */
    if(pthread_create(&newThread, NULL, handler, (void *)clientSocket))
        dieWithMessage("[LOG] pthread_create() failed");

    return 0;
}


//
// SENDING/RECEIVING
//
/*
* Handles and wraps ensuring that the amount specified gets sent.'
* Returns 0 on success and any other value otherwise.
*/
int sendAmount(int destSock, const void *data, size_t amount)
{
    ssize_t currSent, totalSent = 0;

    /* Ensures that all of our data gets sent. */
    while(totalSent < amount)
    {
        if((currSent = send(destSock, ((char *)data + totalSent), (amount - totalSent), 0)) < 0)
            return -1;

        totalSent += currSent;
    }

    if(totalSent > amount)
        printf("[LOG] Warning: More data than expected sent.\n");

    return 0;
}

/*
* Used for converting a request to the endianess used by the network.
*/
void htonRequest(clntReq_t *request)
{
    request->cmd = htonl(request->cmd);
    request->msgLen = htonl(request->msgLen);
}

/*
* Used for converting a response to the endianess used by the network.
*/
void htonResponse(servResp_t *response)
{
    response->resp = htonl(response->resp);
    response->retLen = htonl(response->retLen);
}

void htonGPS(gpsCoord_t *gps)
{
    gps->inval = htonl(gps->inval);
    gps->lat = htonl(gps->lat);
    gps->lon = htonl(gps->lon);
}

/*
* Handles and wraps ensuring that the amount of data specified is recieved.
* Returns 0 on success, 1 upon connection close, any other value otherwise.
*/
int receiveAmount(int srcSock, void *outBuffer, size_t amount)
{
    ssize_t currRecv, totalRecv = 0;

    /* Ensures that a specified amount of data gets recieved. */
    while(totalRecv < amount)
    {
        currRecv = recv(srcSock, ((char *)outBuffer + totalRecv), (amount - totalRecv), 0);

        if(currRecv < 0)
            return -1;
        else if(!currRecv)
            return 1;

        totalRecv += currRecv;
    }

    return 0;
}

/*
* Used for converting a request to the endianess used by the host.
*/
void ntohRequest(clntReq_t *request)
{
    request->cmd = ntohl(request->cmd);
    request->msgLen = ntohl(request->msgLen);
}

/*
* Used for converting a response to the endianess used by the host.
*/
void ntohResponse(servResp_t *response)
{
    response->resp = ntohl(response->resp);
    response->retLen = ntohl(response->retLen);
}

void ntohGPS(gpsCoord_t *gps)
{
    gps->inval = ntohl(gps->inval);
    gps->lat = ntohl(gps->lat);
    gps->lon = ntohl(gps->lon);
}

/*
 * Validates the IP Address
 * Return 0 if invlaid, 1 if valid
 */
int checkip(char* ipAddr)
{
    int i = 0, len = strlen(ipAddr), j;
    char* t;

    if(len > 15 || len < 7)
        return 0;

    t = strtok(ipAddr, ".\n");
    while (t != NULL) {
        j = atoi(t);
        if (j <= 255 && j > 0);
        else if (j == 0 && t[0] != '0')
            return 0;
        else return 0;

        t = strtok((char *) NULL, ".\n");
        i ++;
    }

    if (i != 4) return 0;
    return 1;
}

//
// OTHERS
//
/*
* Simply outputs an error and kills the program upon a bad state.
*/
void dieWithMessage(char *message)
{
    perror(message);
    exit(-1);
}
